var code_editor = function(elem) {
	if (elem.codeEditor) return;
	var s = get_style(elem);
	if (elem.getAttribute('wrap') != 'off')
		elem.setAttribute('wrap', 'off');
	elem.codeEditor = true;

	(function(){
		var rspan=document.createElement('div');
		elem.parentNode.insertBefore(rspan, elem);
		var style = rspan.style;
		style.cssText = 'position:absolute';
		style.fontSize = s.fontSize;
		style.fontFamily = s.fontFamily;
		style.fontStyle = s.fontStyle;
		style.fontWeight = s.fontWeight;
		style.lineHeight = s.lineHeight;
		style.letterSpacing = s.letterSpacing;
		rspan.innerHTML = 'M';
		char_width = rspan.offsetWidth;
		char_height = rspan.offsetHeight;
		if(is_opera())
			rspan.parentNode.insertBefore(elem.parentNode.removeChild(elem), rspan);
		
		rspan.parentNode.removeChild(rspan);

	})();
	
	//alert([char_width, char_height]);
	
		
	var pos = get_position(elem);
	var DX = parseInt(get_style(elem, 'border-left-width'));
	var DY = parseInt(get_style(elem, 'border-top-width'));
	var X0 = pos.left + DX;
	var Y0 = pos.top + DY;
	
	var div = document.createElement('div');
	div.style.cssText = sprintf('position:absolute;z-index:1000;border:2px solid red;left:%d;top:%d;width:%d;height:%d',
		pos.left, pos.top, pos.width-4, pos.height-4);

	var char_width, char_height;
	var cols = Math.floor(elem.clientWidth / char_width);
	var rows = Math.floor(elem.clientHeight / char_height);


	window.setInterval(function(){
		if (elem.clientWidth)
			cols = Math.floor(elem.clientWidth / char_width);
		if (elem.clientHeight)
			rows = Math.floor(elem.clientHeight / char_height);
	}, 1000);

	var move = function(){
		var y1 = elem.scrollTop;
		var x1 = elem.scrollLeft;
		var l = lpos();
		var r = rpos();
		if(!l) { l=-1;}
		if(!r) { r=-1;}
		var y_overflow = (ypos() + 1) * char_height - elem.clientHeight;
		if (y_overflow > 0) {
			y1 += y_overflow - y1 % char_height;
		}
		elem.setSelectionRange(l, r);
		elem.scrollTop = y1;
		elem.scrollLeft = x1;
	};
	
	s = get_style(elem);
	var convert_tabs = function(x) { return x.replace(/\t/g, '    ') };
	var set_caret_abs = function(n) { elem.setSelectionRange(n, n) };
	var dx=0,dy=0;
	var lazy = mklazy();
	var lpos = lazy(function(){ return elem.selectionStart; });
	var rpos = lazy(function(){ return elem.selectionEnd; });
	var text = lazy(
		function(){ return elem.value },
		function(x){		
			var st = elem.scrollTop;
			var sl = elem.scrollLeft;
			var p1 = lpos();
			elem.value = x;
			elem.scrollTop = st;
			elem.scrollLeft = sl;
			//lpos(lpos());
			//rpos(rpos());
			ypos(ypos() + 1);
			move();
			lazy.reset();
		}
	);
	var ltext = lazy(function(){ return text().substr(0, lpos()); });
	var rtext = lazy(function(){ return text().substr(rpos()) }, function(x){elem.value=ltext()+rtext(); move(); 	});
	var selection_text = function(){ return text().substr(lpos(), rpos()-lpos()) };
	var lnlpos = lazy(function(){ return 1+text().lastIndexOf('\n', lpos()-1) });
	var lnrpos = lazy(function(){ return rtext().indexOf('\n', lpos()) });
	var line = lazy(function(){ return text().substr(lnlpos(), lnrpos() - lnlpos() + 1) });
	var col = lazy(function(){ return Math.max(0, lpos() - lnlpos()) });
	var row = lazy(function(){ return substr_count('\n', ltext()) });
	var rowtop = lazy(function(){ return Math.round(elem.scrollTop/char_height) });
	var ypos = lazy(function(){ return row()-rowtop() });
	var height = elem.offsetHeight;

	var insert = function(x){
		var y1 = elem.scrollTop;
		var x1 = elem.scrollLeft;
		var delta = elem.scrollTop + height - (row()+2)*char_height;
		elem.value = ltext() + x + rtext();
		rpos(rpos()+x.length);
		lpos(rpos());
		move();
		elem.scrollTop = delta > 0 ? y1 : char_height * Math.ceil((y1 - delta)/char_height);
		elem.scrollLeft = x1;
	};
	
	var marker = document.createElement('div');
	marker.style.cssText='display:none; position:absolute; width:20px; height:20px; border:1px solid black; opacity:0.3; background-color:yellow';
	marker.style.height = (char_height - 2) + 'px';
	marker.style.width = char_width + 'px';

	elem.parentNode.insertBefore(marker, elem);
	var cnt = 1000;
	var late = mklate(50);

	lazy.reset(function(){ marker.style.display='none' });

	var event = mkevent();
	
	event.add(elem, 'DOMMouseScroll', function(){ marker.style.display='none' });
	event.add(elem, 'mousewheel', function(){ marker.style.display='none' });
	event.add(elem, 'scroll', function(){ marker.style.display='none' });
	event.add(elem, 'click', function(){ lazy.touch(); update_marker_position() });
	
	var bs_del_spaces;
	var event_lock = false;
	
	var oHelp = document.createElement('div');
	oHelp.style.cssText = '\
		position:absolute;\
		opacity:0.8;\
		background-color:blue;\
		color:white;\
		border:1px solid black;\
		font: normal 11px Arial;\
	';
	oHelp.style.marginTop = (elem.offsetHeight + 2) + 'px';
	
	
	var late_help = mklate(500);
	var late_hide_help = mklate(3000);
	
	var help = function(x, delay) {
		if (x === undefined) {
			if (oHelp.parentNode)
				oHelp.parentNode.removeChild(oHelp);
			return;
		}
		oHelp.innerHTML = sprintf.apply(this, arguments);
		if (!oHelp.parentNode)
			elem.parentNode.insertBefore(oHelp, elem);
		late_hide_help(delay || 3000, help)();
	};

	event.add(elem, 'keyup', function(e){
		help();
	});


	var block_selection;
	var marker_block = {};
	var block_mark_direction_x = 1;
	var block_mark_direction_y = 1;
	
	var reset_block = function() {
		block_selection = false;
		set_marker_block();				
	};
	
	var mark_block = function(dw, dh) {
		var b = marker_block;
		if (!block_selection) {
			block_selection = true;
			b.x = col();
			b.y = row();
			b.w = 0;
			b.h = 1;
			if (dh < 0)
				--b.y;
			block_mark_direction_y = dh < 0 ? -1 : 1;
		}
		if (b.w == 0) block_mark_direction_x = dw < 0 ? -1 : 1;
		if (b.h == 0) block_mark_direction_y = dh < 0 ? -1 : 1;			
		if (block_mark_direction_x > 0) b.w += dw; else { b.x += dw; b.w += -dw; }
		if (block_mark_direction_y > 0) b.h += dh; else { b.y += dh; b.h += -dh; }
		set_marker_block(b.x, b.y, b.w, b.h);
	};


	var key_cnt = 10000;
	
	var late_set_message = mklate(100)(set_message);
	var late_alert = function() {
		var args = arguments;
		window.setTimeout(function(){
			window.alert.apply(window, args);
		}, 100);
	};
	

/*
	event.add(document, 'keydown', function(e){
		var key = get_keystroke_name(e);
		var out = {
			cnt: key_cnt++,
			key: key,
			shiftKey: e.shiftKey
		};
		set_message(out);
	});
*/


/*
	event.add(elem, 'keydown', function(e){
		var key = get_keystroke_name(e);

		var out = {
			cnt: key_cnt++,
			key: key,
		};

		
		//late_set_message(out);
		
		switch (key) {
			case 'SHIFT_SLASH':
				alert('shift slash!');
				e.stopPropagation();
				e.preventDefault();
				return !1;
		}
	});
*/



	if(0)event.add(elem, 'keydown', function(e){
		var key = get_keystroke_name(e);
		if (key == 'CTRL')
			help('Ctrl-H: search and replace');
		else {
			help();
		}
	});

	
	var history = [];
	
	event.add(elem, 'keypress keydown', function(e){
		var key = get_keystroke_name(e);

		switch (key) {
			case 'CTRL': case 'SHIFT': case 'ALT': return;
		}
		
		if (key != 'TAB')
			window.setTimeout(function(){ bs_del_spaces = null }, 0);

		switch (key) {
			case 'ALT_RIGHT': mark_block( 1,  0); return !1;
			case 'ALT_LEFT' : mark_block(-1,  0); return !1;
			case 'ALT_UP'   : mark_block( 0, -1); return !1;
			case 'ALT_DOWN' : mark_block( 0,  1); return !1;

			case 'CTRL_V':
				var b = marker_block;
				
				if (!b.text)
					break;
				
				var xx = b.text.split(/\r?\n/);
				
				head(rtext(), xx.length, function(t1, t2){
					var c = col();
					var y, yy = t1.split(/\r?\n/);
					xx[0] = xx[0] + yy[0];
					for (var i = 1; i < yy.length; ++i)
						xx[i] = substr_pad(yy[i], 0, c) + xx[i] + yy[i].substr(c);
					
					if (xx.length > yy.length) {
						var pad = strdup(' ', c);
						for (; i < xx.length; ++i)
							xx[i] = pad + xx[i];
					}
						
					rtext(xx.join('\n').replace(/[ \t]+$/mg, '') + t2);
				});
				return !1;
				

			
			case 'CTRL_C':
				if (!block_selection) {
					marker_block.text = undefined;
					return;
				}				

				(function(b){
					var t = head(rtext(), b.h);
					var out = [];
					var xx = t.split(/\r?\n/);
					out[out.length] = substr_pad(xx[0], 0, b.w);
					for (var i = 1; i < xx.length; ++i)
						out[out.length] = substr_pad(xx[i], b.x, b.w);
					b.text = out.join('\n');
					//late_alert(b.text.replace(/^.*$/mg, '[$&]'));
				})(marker_block);

				return !1;
			
			
			
		}

while_block_selection:
		while (block_selection) {
			switch (key) {
				default:
					block_selection = false;
					set_marker_block();
					break while_block_selection;
				

				case 'CTRL_V':
					(function(b){
						var xx = head(rtext(), b.h);
						var out = [];
						for (var i = 0; i < xx.length; ++i)
							out[out.length] = xx[i].substr(b.x, b.w);
						b.text = out.join('\n');
					})(marker_block);
					break;
					
				case 'DEL':
					(function(b){
						var x0 = b.x, x1 = b.x + b.w - 1, x = col();
						head(rtext(), b.h, function(t1, t2){
							t1 = t1.replace(/.|(\r?\n)/g, function($0, $1) {
								if ($1) { x = 0; return $0; }
								var _x = x++;
								return _x >= x0 && _x <= x1 ? '' : $0;
							});
							rtext(t1 + t2);
						});
					})(marker_block);
					break;
			}
			reset_block();
			return !1;
		}
			
		switch (key) {
			default:
				lazy.touch();
				update_marker_position();
				break;
				
			case 'CTRL_H':
				break;
				dialog_ui(function(ui){
					ui.parent(elem);
					var a = ui.text('Find what');
					var b = ui.text('Replace with');
					var b_re = ui.checkbox('Regular expression');
					var b_cs = ui.checkbox('Case sensitive');
					var b_whole = ui.checkbox('Whole words');
					ui.button('Replace all', function(){
						var flags = [];
						flags[flags.length] = 'g';
						if (!b_cs)
							flags[flags.length] = 'i';
						var re = a.value;
						if (!b_re)
							re = re_escape(re);
						if (b_whole)
							re = '\\b' + re + '\\b';
						re = new RegExp(re, flags.join(''));
						var y = b.value;
						var count = 0;
						elem.value = elem.value.replace(re, function(){
							++count;
							return y;
						});
						help('Number of matches: ' + count, 1000);
						return false;
					});
					ui.button('Cancel');
					ui.close(function(){
						move();
					});
				});
				return !1;
				
			case 'BS':
				//history.push(text());
				
				//alert(bs_del_spaces);
				
				if (bs_del_spaces) {
					var x = lpos() - bs_del_spaces;
					text(text().substr(0,lpos()-bs_del_spaces) + text().substr(rpos()));					
					lpos(x ? x : -1); rpos(x ? x : -1);
					//text(text().substr(0, x) + text().substr(rpos()));					
					move();
					bs_del_spaces = null;
					return !1;
				}
			
				tab = col()%4;
				if(tab==0 && text().substr(lpos()-4,4)=='    '){
					var x = lpos()-4;
					text(text().substr(0,lpos()-4) + text().substr(rpos()));
					if(!x) x=-1;
					lpos(x); rpos(x);
					move();
					return !1;
				}
				update_marker_position();
				break;

			case 'DEL':
				if (lpos() != rpos())
					return;
				tab = col() % 4;
				if (tab == 0 && rtext().substr(0, 4) == '    '){
					text(text().substr(0, lpos()) + text().substr(rpos() + 4));
					/*
					var st = elem.scrollTop;
					var sl = elem.scrollLeft;
					var p1 = lpos();
					elem.value = text().substr(0,lpos()) + text().substr(rpos()+4)
					elem.scrollTop = st;
					elem.scrollLeft = sl;
					lpos(lpos());
					rpos(rpos());
					ypos(ypos() + 1);
					move();
					lazy.reset();
					*/
					//text();
					//lazy.touch();
					return !1;
				}
				update_marker_position();
				break;

			case 'LEFT':
				tab = col()%4;
				if(tab==0 && text().substr(lpos()-4,4)=='    '){
					var l = lpos()-4;
					var r = rpos()-4;
					if(!l) l=-1;
					if(!r) r=-1;
					lpos(l); rpos(r);
					move();
					return !1;
				}
				var x = lpos()-1;
				if(!x)x=-1;
				lpos(x); rpos(x);
				update_marker_position();
				break;
		
			case 'CTRL_RIGHT':
				var s = text().substr(lpos(),100);
				var p = s.search(/.[A-ZА-Я0-9]|[\n\(\[\{,.'"_#\/\\:\s][a-zа-я0-9]/);		
				if(p >= 0) {
					++p;
					p = lpos() + p;
					lpos(p);
					rpos(p);
					move();
					return !1;
				}
				break;
				
			case 'CTRL_LEFT':
				var s = text().substr(0, lpos()-1).substr(-100);
				var re = /[A-ZА-Я0-9]|[\n\(\[\{,.'"_#\/\\:\s][a-zа-я0-9]/;
				var m = s.split(re);
				if(m) {
					var p = m[m.length-1].length;
					p++;
					p = lpos() - p - 1;
					if(p>0) {
						lpos(p);
						rpos(p);
						move();
						return !1;
					}
				}
				break;
				
			case 'RIGHT':
				tab = col()%4;
				if(tab==0 && text().substr(lpos(),4)=='    ') {
					lpos(lpos()+4);
					rpos(rpos()+4);
					move();
					return !1;
				}
				lpos(lpos()+1);
				rpos(lpos()+1);
				update_marker_position();
				break;

			case 'SHIFT_TAB':
				if (!(lpos() != rpos() && col() == 0))
					return;

				var t = text();
				var t1 = t.substr(0, lpos());
				var t2 = t.substr(lpos(), rpos() - lpos() - 1);
				var t3 = t.substr(rpos() - 1);
				t2 = t2.replace(/^ {1,4}/mg, '');
				rpos(lpos() + t2.length + 1);
				text(t1 + t2 + t3);
				move();
				return !1;


			case 'SHIFT_SLASH':
				if (!(lpos() != rpos() && col() == 0))
					return;
				var t = text();
				var t1 = t.substr(0, lpos());
				var t2 = t.substr(lpos(), rpos() - lpos() - 1);
				var t3 = t.substr(rpos() - 1);
				var t2_old_length = t2.length;
				t2 = t2.replace(/^([ \t]*)\/\//mg, '$1');
				//rpos(lpos() + t2.length + 1);
				var _l = elem.selectionStart;
				var _r = elem.selectionEnd + t2.length - t2_old_length;				
				
				text(t1 + t2 + t3);
				elem.setSelectionRange(_l, _r);
				//move();
				return !1;


			case 'SLASH':
				if (!(lpos() != rpos() && col() == 0))
					return;
				var t = text();
				//alert(lpos());
				var t1 = t.substr(0, lpos());
				var t2 = t.substr(lpos(), rpos() - lpos() - 1);
				var t3 = t.substr(rpos() - 1);
				
				var l = Infinity;
				t2.replace(/^( +)[^ ]/mg, function($0, $1) {
					l = Math.min(l, $1.length);
					return $0;
				});
				if (l > 30) l = 0;
				
				var t2_old_length = t2.length;
				
				var empty_line = strdup(' ', l) + '//';
				
				t2 = t2.replace(/^( *)/mg, function($0){
					if ($0.length < l) return empty_line;
					return $0.substr(0, l) + '//' + $0.substr(l);
				});
				
				var _l = elem.selectionStart;
				var _r = elem.selectionEnd + t2.length - t2_old_length;				
				text(t1 + t2 + t3);
				elem.setSelectionRange(_l, _r);
				return !1;
				
			case 'TAB':
				if (lpos() != rpos() && col() == 0) {
					var t = text();
					var t1 = t.substr(0, lpos());
					var t2 = t.substr(lpos(), rpos() - lpos() - 1);
					var t3 = t.substr(rpos() - 1);
					t2 = t2.replace(/^/mg, '    ');
					rpos(lpos() + t2.length + 1);
					text(t1 + t2 + t3);
					move();
					return !1;
				}
			
				lazy.reset();
				tab = col() % 4;
				bs_del_spaces = 4 - tab;
				
				//alert(bs_del_spaces);
				
				var t = text().substr(0, lpos()) + '     '.substr(0,4-tab) + text().substr(rpos());
				lpos(lpos()+4-tab);
				rpos(rpos()+4-tab);
				text(t);
				move();
				lazy.touch();
				return !1;

			case 'ENTER':
				lazy.reset();
				var nl = is_opera() ? '\r\n' : '\n';
				var t = ltext();
				var ins;
				for (var i = t.length - 1, j = i + 1; i >= 0; --i) {
					switch (t[i]) {
						case '\n': 
							ins = t.substr(i, j - i);
							break;
						case ' ':
							continue;
						default:
							j = i;
							continue;
					}
					break;
				}
				var st = elem.scrollTop;
				var sl = elem.scrollLeft;
				var p1 = lpos();
				elem.value = [t, ins, rtext()].join('');
				elem.scrollTop = st;
				elem.scrollLeft = sl;
				lpos(lpos()+ins.length);
				rpos(rpos()+ins.length);
				ypos(ypos() + 1);
				move();
				lazy.reset();

				return !1;
			
		}
	});

	

	var update_marker_position = mklate(100)(function(){
		lazy.reset();
		var a, b, c, c0;
		var y = row(), ymin = rowtop(), x;
		var t = text();
		var d = 1;
		var i = lpos() - 1;
		a = b = c = i;
		var A, B, ch = t.substr(i, 1);
		var _dx = 0;
		var ch2;
		switch (ch){
			default: return;
			
			case '(': case '{': case '[':
				switch (ch){
					case '(': A = '('; B = ')'; ch2=')'; break;
					case '{': A = '{'; B = '}'; ch2='}'; break;
					case '[': A = '['; B = ']'; ch2=']'; break;
				}
				dx = 1;
				var imax = t.length - 1;
				ymax = row() + rows - ypos();
				c = i - col();
				a = b = -1;
				c = t.lastIndexOf('\n', i);
				while (i <= imax && y <= ymax) {
					if (d == 0){ x = i - c0 - 1; break }
					if (a <= i) { a = t.indexOf(A, i + 1); if (a < 0) a = Infinity; }
					if (b <= i) { b = t.indexOf(B, i + 1); if (b < 0) b = Infinity; }
					if (c <= i) { c0 = c; c = t.indexOf('\n', i + 1); if (c < 0) c = Infinity; }
					if (c < a && c < b) { ++y; i = c; continue; }
					if (a < b && a < c) { ++d; i = a; continue }
					if (b < a && b < c) { --d; i = b; continue }
					break;
				}
				break;

			case ')': case '}': case ']':
				switch (ch) {
					case ')': A = '('; B = ')'; ch2 = '('; break;
					case '}': A = '{'; B = '}'; ch2 = '{'; break;
					case ']': A = '['; B = ']'; ch2 = '['; break;
				}
				dx = 2;
				while (i >= 0 && y >= ymin){
					if (d == 0){ x = i - c - 1; break }
					if (a >= i) a = t.lastIndexOf(A, i - 1);
					if (b >= i) b = t.lastIndexOf(B, i - 1);
					if (c >= i) c = t.lastIndexOf('\n', i - 1);
					if (c > a && c > b){ --y; i = c; continue }
					if (b > a && b > c){ ++d; i = b; continue }
					if (a > b && a > c){ --d; i = a; continue }
					break;
				}
				break;
		}
		if (d > 0) x = undefined;
		//alert(ch);
		set_marker_block(x, y, 1, 1, ch2);
	});
	
	var set_marker_block = function(x, y, w, h, ch) {
		if (x === undefined) {
			marker.style.display = 'none';
			return;
		}
		if (is_string(w)) {
			ch = w;
			w = undefined;
		}
		if (w === undefined) w = 1;
		if (h === undefined) h = 1;
		
		var marker_x = elem.offsetLeft + DX + x * char_width - elem.scrollLeft;
		var marker_y = elem.offsetTop + DY + char_height * y - elem.scrollTop;
		var marker_w = char_width * w;
		var marker_h = char_height * h;
	
		if (ch) {
			marker_y += 3;
			marker_x += 1;
			marker_w -= 2;
			marker_h -= 6;
		}
		
		if (is_chrome()) {
			marker_y += 2;
			marker_x += 1;
		}
		
		
		if (is_opera()) marker_y += 1;
		marker.style.left = marker_x + 'px';
		marker.style.top = marker_y + 'px';
		marker.style.width = marker_w + 'px';
		marker.style.height = marker_h + 'px';
		marker.style.display = 'block';
	};
	
};
	
	

window.setTimeout(function(){
	var init_editors = function(){
		var x, xx = document.getElementsByTagName("TEXTAREA");
		for (var i = 0, max = xx.length; i < max; ++i) {
			x = xx[i];
			if (x.getAttribute('wrap') == 'on') continue;
			if (/courier|mono|lucida/i.test(get_style(x, 'font-family')))
				(function(x){ window.setTimeout(function(){ code_editor(x) }, 0) })(x);
		}
	};	
	init_editors();
	window.setTimeout(function(){
		window.setInterval(function(){ init_editors() }, 3000);
	}, 2000);
}, 100);








